﻿using System;
using System.Collections.Generic;
using System.Text;

namespace System.Data.SugarClient.Soap.V1
{
	internal class V1GetEntryListAction : V1ModuleAction
	{

		#region Constructors

		internal V1GetEntryListAction(
			SugarSoapV1.sugarsoap service,
			string session,
			string moduleName,
			string criteria,
			string order,
			int offset,
			string[] fields,
			int limit,
			bool deleted)
			: base(service, session, moduleName) 
		{
			this._criteria = criteria;
			this._order = order;
			this._offset = offset;
			this._fields = fields;
			this._limit = limit;
			this._deleted = deleted;

			this.CheckFieldAvailablility();
		}

		#endregion

		#region Fields

		private readonly string _criteria;
		private readonly string _order;
		private readonly int _offset;
		private readonly string[] _fields;
		private readonly int _limit;
		private readonly bool _deleted; 

		#endregion

		#region Properties

		public string Criteria
		{
			get { return _criteria; }
		}

		public string Order
		{
			get { return _order; }
		}

		public int Offset
		{
			get { return _offset; }
		}

		public string[] Fields
		{
			get { return _fields; }
		}

		public int Limit
		{
			get { return _limit; }
		}

		public bool Deleted
		{
			get { return _deleted; }
		}

		#endregion

		protected override V1ActionResult ExecuteInner()
		{
			var result =
				this.SoapService.get_entry_list(
					this.Session,
					this.Module,
					this.Criteria,
					this.Order,
					this.Offset,
					this.Fields,
					this.Limit,
					(this.Deleted) ? 1 : 0);

			return new V1GetEntryListActionResult(this, result);
		}

		internal protected string[] CheckFieldAvailablility()
		{
			V1GetModuleFieldsAction action = new V1GetModuleFieldsAction(this.SoapService, this.Session, this.Module);
			V1GetModuleFieldsActionResult result = action.Execute() as V1GetModuleFieldsActionResult;
			if (!result.Success) throw new SugarException("Can not get field list");
			List<string> target = new List<string>(this.Fields);
			foreach (SugarVarDef vardef in result.VarDefs)
			{
				target.Contains(vardef.Name);
				target.Remove(vardef.Name);
				if (target.Count == 0) return new string[]{};
			}

			if (0 < target.Count) return target.ToArray();
			else return new string[] { };
		}
	}

	internal class V1GetEntryListActionResult : V1ModuleActionResult, IGetEntryListActionResult
	{

		internal V1GetEntryListActionResult(
			V1GetEntryListAction action,
			SugarSoapV1.get_entry_list_result result)
			: base(action)
		{
			this._result = result;
		}

		private readonly SugarSoapV1.get_entry_list_result _result;

		internal SugarSoapV1.get_entry_list_result SoapResult
		{
			get { return _result; }
		}

		public virtual bool Success
		{
			get
			{
				if (SoapResult.error == null) return true;
				else if (SoapResult.error.number == null) return true;
				else if ("0".Equals(SoapResult.error.number)) return true;
				else return false;
			}
		}

		public V1GetEntryListAction Action
		{
			get { return base.Action as V1GetEntryListAction; }
		}

		#region IGetEntryListActionResult メンバ

		public int Count
		{
			get { return this.SoapResult.result_count; }
		}

		public int FieldCount
		{
			get { return this.Action.Fields.Length; }
		}

		public object this[int row, int col]
		{
			get { return GetValueOf(row, col); }
		}

		#endregion

		#region Methods

		#region IGetEntryListActionResult メンバ

		public  string GetFieldNameAt(int index)
		{
			return this.Action.Fields[index];
		}

		public object GetValueOf(int index, int ordinal)
		{
			SugarSoapV1.entry_value entry = this.SoapResult.entry_list[index];
			if (entry == null)
			{
				throw new IndexOutOfRangeException();
			}
			var fieldName = GetFieldNameAt(ordinal);
			SugarSoapV1.name_value value = Array.Find<SugarSoapV1.name_value>(entry.name_value_list, (v) => v.name.Equals(fieldName));
			if (value == null)
			{
				Trace.Debug("Field \"{0}\" is not found.", fieldName);
			}
			return (value != null) ? ((!string.IsNullOrEmpty(value.value)) ? value.value : null) : null;
		}

		public SugarVarDef GetVarDefAt(int ordinal)
		{
			string fieldName = GetFieldNameAt(ordinal);
			SugarSoapV1.field field = Array.Find<SugarSoapV1.field>(this.SoapResult.field_list, (v) => v.name.Equals(fieldName));
			if (field == null)
			{
				// if soap retunr no data, _soapResult.field_list doesnt contains no entry,
				// TODO this causes too many soap round trips.
				V1GetModuleFieldsAction action = new V1GetModuleFieldsAction(this.Action.SoapService, this.Action.Session, this.Action.Module);
				V1GetModuleFieldsActionResult result = action.Execute() as V1GetModuleFieldsActionResult;
				for (int i = 0; i < result.VarDefs.Count; ++i)
				{
					if (fieldName.Equals(result.VarDefs[i].Name)) return result.VarDefs[i];
				}
				throw new IndexOutOfRangeException();
			}
			return SugarVarDef.Create(field);
		}

		public int IndexOf(string field)
		{
			return Array.IndexOf(this.Action.Fields, field);
		}

		public ISugarSoapAction NextPageAction()
		{
			if (this.SoapResult.next_offset < 0) return null;

			V1GetEntryListAction action = this.Action as V1GetEntryListAction;
			return
				new V1GetEntryListAction(
					action.SoapService,
					action.Session,
					action.Module,
					action.Criteria,
					action.Order,
					this.SoapResult.next_offset,
					action.Fields,
					action.Limit,
					action.Deleted);
		}

		#endregion

		#endregion

		
	}

}
